#include "TimerChannel.h"
#include <algorithm>
#include <cstdio>
#include <sys/timerfd.h>
#include <unistd.h>
#include "DateTime.h"
#include "EventLoop.h"
#include "PlatLogger.h"
#include "Poller.h"
#include "Util.h"
#include "XnccType.h"

namespace xncc {
namespace foundation {
    using shared::CommonBusi;
    TimerChannel::TimerChannel(EventLoop* loop)
        : Channel(loop,
                  loop->mode() != types::IO_MULTI_MODE::value::NO_IO_MULTI ? CommonBusi::createTimerfd() :
                                                                             NOBLOCKPOLLER_TIMER_FD,
                  types::FROM_OTHER_THREAD)
    {
        TRACE_INFO << "create TimerChannel bind loop id:" << loop->id() << " fd:" << fd()
                   << " loop type:" << types::IO_MULTI_MODE::toString(loop->mode());
    }

    TimerChannel::~TimerChannel()
    {
        if (loopType() != types::IO_MULTI_MODE::value::NO_IO_MULTI) {
            ::close(fd());
        }
    }
    types::timer_id_t TimerChannel::addTimer(types::Task tsk, int64_t when, int64_t interval)
    {
        auto timer = std::make_shared<Timer>(std::move(tsk), when, interval);
        TRACE_INFO << "add timer when:"_s << when << " time:" << types::LocalTimeStamp(when).toStringWithMicroSecond()
                   << " interval:"_s << interval << "us timerid:"_s << timer->ID();
        RunTask([this, timer]() { addTimerInLoop(timer); });
        return timer->ID();
    }
    void TimerChannel::cancel(types::timer_id_t timerId)
    {
        TRACE_INFO << "cancel timer timerid:"_s << timerId;
        RunTask([this, timerId]() { cancelInLoop(timerId); });
    }

    void TimerChannel::addTimerInLoop(const Timer::s_ptr& timer)
    {
#ifdef XNCC_DEBUG
        loop_->assertInLoopThread(__FUNCTION__);
#endif
        bool earliestChanged = insert(timer);
        if (earliestChanged) {  // 需要最先被触发 必须重置timerfd的设置
            if (loopType() != types::IO_MULTI_MODE::value::NO_IO_MULTI) {
                resetTimerfd(timer->getExpir());
            }
        }
    }

    void TimerChannel::cancelInLoop(types::timer_id_t timerId)
    {
#ifdef XNCC_DEBUG
        loop_->assertInLoopThread(__FUNCTION__);
#endif
        auto iter = timers_.find(timerId);
        if (iter != timers_.end()) {
            // 虽然在timer中erase掉了，但是最小堆的数据还存在
            timers_.erase(iter);
        }
        else {
            TRACE_ERROR << "call cancel but not found in timers, timerId:"_s << timerId;
        }
    }
    std::vector<Timer::s_ptr> TimerChannel::getExpired(int64_t now)
    {
        std::vector<Timer::s_ptr> expired;
        while (true) {
            if (minHeapTimers_.empty()) {
                break;
            }
            auto timer = minHeapTimers_.top();
            /// id不存在的时候，说明有可能已经被erase掉了
            if (timers_.find(timer->ID()) == timers_.end()) {
                minHeapTimers_.pop();
                continue;
            }
            if (timer->getExpir() <= now) {
                expired.push_back(timer);
                minHeapTimers_.pop();
            }
            else {
                break;
            }
        }
        return expired;
    }

    bool TimerChannel::insert(const Timer::s_ptr& timer)
    {
#ifdef XNCC_DEBUG
        loop_->assertInLoopThread(__FUNCTION__);
#endif
        bool         earliestChanged = false;
        Timer::s_ptr oldTopTimer     = nullptr;
        if (!minHeapTimers_.empty()) {
            oldTopTimer = minHeapTimers_.top();
        }
        minHeapTimers_.push(timer);
        auto newTopTimer = minHeapTimers_.top();

        if (oldTopTimer == nullptr) {
            earliestChanged = true;
        }
        else {
            if (oldTopTimer->ID() != newTopTimer->ID()) {
                // 判断最先触发的timer是不是变了
                // 新插入的定时器和以前的比，可能是需要最先触发的
                earliestChanged = true;
            }
        }
        // 如果是周期定时器，可能存在多次插入的现象，插入失败也没有关系， timer的内容已经被restart了
        timers_.insert({timer->ID(), timer});
        return earliestChanged;
    }

    struct timespec TimerChannel::howMuchTimeFromNow(int64_t when)
    {
        int64_t microseconds                        = when - types::DateTime::timeMicro();
        microseconds                                = std::max<int64_t>(microseconds, Timer::resolve_precision);
        struct timespec   ts                        = {};
        constexpr int64_t microSecondsPerSecond     = 1000 * 1000;
        constexpr int64_t nanoSecondsPerMicroSecond = 1000;

        ts.tv_sec  = static_cast<time_t>(microseconds / microSecondsPerSecond);
        ts.tv_nsec = static_cast<long>((microseconds % microSecondsPerSecond) * nanoSecondsPerMicroSecond);
        return ts;
    }

    bool TimerChannel::readfd()
    {
        uint64_t howmany = 0;
        ssize_t  nrRet   = ::read(fd(), &howmany, sizeof howmany);
        return nrRet == sizeof howmany;
    }

    void TimerChannel::resetTimerfd(int64_t expiration)
    {
        struct itimerspec newValue = {};
        struct itimerspec oldValue = {};
        memset(&newValue, 0, sizeof newValue);
        memset(&oldValue, 0, sizeof oldValue);
        newValue.it_value = howMuchTimeFromNow(expiration);
        int ret           = ::timerfd_settime(fd(), 0, &newValue, &oldValue);
        if (ret != 0) {
            SYSTEM_ERROR << "timerfd_settime()"_s;
        }
    }
    void TimerChannel::handleRead()
    {
#ifdef XNCC_DEBUG
        loop_->assertInLoopThread(__FUNCTION__);
#endif
        int64_t now = types::DateTime::timeMicro();
        readfd();
        doTimerFuncs(now);
    }

    void TimerChannel::doTimerFuncs(int64_t now)
    {
        std::vector<Timer::s_ptr> expired = getExpired(now);
        for (const auto& iter : expired) {
            iter->run();
        }
        reset(expired, now);
    }

    void TimerChannel::reset(const std::vector<Timer::s_ptr>& expired, int64_t now)
    {
        for (const auto& iter : expired) {
            if (iter->isPeriod() && timers_.find(iter->ID()) != timers_.end()) {
                iter->restart(now);
                /// 周期性的要重新insert
                insert(iter);
            }
        }
        if (loopType() != types::IO_MULTI_MODE::value::NO_IO_MULTI) {
            if (!timers_.empty()) {
                Timer::s_ptr nextExpire = minHeapTimers_.top();
                if (nextExpire->getExpir() > 0) {
                    resetTimerfd(nextExpire->getExpir());
                }
            }
        }
    }
}  // namespace foundation
}  // namespace xncc